/*
 *  GameLayer.m
 *  pogostick
 *
 *  Created by daddy (chuck@kollar.com) on 9/18/09.
 *  Copyright 2009 Charles P. Kollar All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#import <OpenGLES/ES1/gl.h>
#import <math.h>
#import "OpenGL_Internal.h"

#import "common.h"
#import "GameLayer.h"
#import "Menu.h"
#import "Level.h"
#import "Person.h"
#import "Bug.h"
#import "PogoStick.h"
#import "BodyJoint.h"
#import "VerticalPlatform.h"
#import "ArcPlatform.h"
#import "LunarLander.h"
#import "PillCage.h"


////
//// Some notes that I made in trying to get the provisioning stuff working. In my estimation, apple makes
//// this more painfull than it needs to be....
////
//// Framework location...
// /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.2.1.sdk/System/Library/Frameworks/AVFoundation.framework
//// Frameworks needed... (device is currently 3.1)
// AVFoundation.framework
// AudioToolBox.framework
// OpenAL.framework
// UIKit.framework
// Foundation.framework
// CoreGraphics.framework
// OpenGLES.framework
// QuartzCore.framework

// ipod password 7884
// Info.plst Bundle identifier: com.kollar.${PRODUCT_NAME:identifier}

//// It seems that the Project setting below results in a linker error with -lcrt10.1.5.0 missing?!
// Using the deployment target of 10.5 works though...
// Project > Edit Project Settings > Mac OS X Deployment Target > Mac OS X 10.6

//// Fixing the executable was signed with invalid entitlements error...
// In the File Menu, select New File -> iPhone OS -> Code Signing -> Entitlements. Name the file “Entitlements.plist" and click ‘Finish’. This creates a copy of the default entitlements file within the project.
// Select the new Entitlments.plist file and uncheck the “get-task-allow” property. Save the Entitlements.plist file.
// Select the Target and open the Build settings inspector. In the ‘Code Signing Entitlements’ build setting, type in the filename of the new Entitlements.plist file including the extension. There is no need to specify a path unless you have put the Entitlements.plist file somewhere other than the top level of the project.

// It seems that if you change the last value in the function call gluPerspective in the method set3Dprojection
// in the file director.m to a number which is one larger than the maximum eye to center Z that you want (here
// zoomOutLimit), then it works without going "to heck" (as we have noted above). For example I have a zoom out
// to 2000. when the number in the function call to set2Dprojection is set to 2001.

#define ZOOMOUTLIMIT                    2000.0f
#define ZOOMINLIMIT                     415.0f

#define MARGIN                          6

#define INIT_ZOOM_SLIDER_CTL_VALUE      1.0f
#define INIT_SPRING_SLIDER_CTL_VALUE    0.25f


// set in menu.m by the user from yes/no buttons...
extern int soundp, musicp;

// This is a global variable that should be accessed in lots of places.
// It keeps track of the user's score. Various functions and methods will
// need to add and subtract to it depending on what is happening in the game.
int currentScore;

// This file contains two different class definitions. One (OverlayLayer) defines
// all of the things that overlay the game the give/take feedback to/from the user.
// The other layer (GameLayer) is where the action takes place. Since there are two
// involved we put this "shared data" as globals to this file. The Overlay Layer
// allows the user to set these, and the Game Layer allows the Game to use them.
// THESE TWO VARIABLES SHOULD NOT BE REFERENCED OUTSIDE OF THIS FILE....
float cameraZAxisZoomFactor;
float stickSpringPower;


////
//// The layer with the controls and indicators. This layer doesn't zoom...
////
@implementation OverlayLayer

@synthesize menuX;
@synthesize menuY;
@synthesize menuSprite;
@synthesize labelAtlas;
@synthesize scoreImageView;
@synthesize springSliderCtl;


// Information about the amulet...
BOOL amuletFound;
id amulet;
Sprite *amuletSprite;

-(id) init {
	if (self = [super init]) {
        CGSize screenSize = [[Director sharedDirector] displaySize];
        isTouchEnabled = YES;
        
        // Touching this icon will cause the program to go to the menu page....
        menuSprite = [Sprite spriteWithFile:@"menuIcon.png"];
        CGSize size = menuSprite.contentSize;
        menuY = size.width;
        menuX = size.height;
        [self addChild:menuSprite];
        [menuSprite setPosition: ccp(screenSize.height-size.width/2, size.height/2)];
        [menuSprite setVisible: NO];

        // current game score label...
        labelAtlas = [LabelAtlas labelAtlasWithString:@"0000" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'];
        [self addChild:labelAtlas];// tag:kTagLabelScore];
        [labelAtlas setPosition:ccp(2,2)];
        
        CGRect frame;
        // There is one slider which allows the user to control the springiness of the pogostick...
        
        frame = CGRectMake(menuY + 20.0f, screenSize.height - 20.0f, screenSize.width*3/4 - 10.0f, 20.0f);
        springSliderCtl = [[UISlider alloc] initWithFrame:frame];
        [springSliderCtl addTarget:self action:@selector(springSliderAction:) forControlEvents:UIControlEventValueChanged];
        // in case the parent view draws with a custom color or gradient, use a transparent color
        springSliderCtl.backgroundColor = [UIColor clearColor];
        springSliderCtl.minimumValue = 0.0f;
        springSliderCtl.maximumValue = 1.0f;
        springSliderCtl.continuous = YES;
        springSliderCtl.value = INIT_SPRING_SLIDER_CTL_VALUE;
        springSliderCtl.tag = kTagSpringSlider;	// tag this view for later so we can remove it from recycled table cell
        [[[[Director sharedDirector] openGLView] window] addSubview: springSliderCtl];
        
        amuletSprite = [[Sprite spriteWithFile:@"amulet.png"] retain];
        [self addChild:amuletSprite];
        size = amuletSprite.contentSize;
        [amuletSprite setPosition:ccp(size.width/2, screenSize.width - size.height/2)];
        [amuletSprite setVisible: amuletFound];
        
        // Max umber of lives that the player can get...
        // create them all here, but only make them visible as needed...
        [self setScoreImageView: [[NSMutableArray alloc] initWithCapacity: MAX_LIVES]];
        for (int i=1, k=MAX_LIVES+2+size.width; i<=MAX_LIVES; i++,k+=MAX_LIVES+2) {
            Sprite *sprite = [[Sprite spriteWithFile:@"life.png"] retain];
            [self addChild:sprite];
            CGSize size = menuSprite.contentSize;
            [sprite setPosition:ccp(k, screenSize.width - size.height/2)];
            if (i%5 == 0) k+=size.height/2;
            [scoreImageView addObject: sprite];
            [sprite setVisible: NO];
            [sprite release];
        }
    }
    return self;
}

//
// Deal with the fact that sliders are located on an OpenGL View...

-(void) onEnter
{
	[super onEnter];
    [[[[Director sharedDirector] openGLView] window] addSubview: springSliderCtl];
}

-(void) onExit
{
    [springSliderCtl removeFromSuperview];
	
    [super onExit];
}

-(void) springSliderAction:(id) __attribute__ ((unused)) sender
{
    stickSpringPower = 50.0f*springSliderCtl.value + 20.0f;
    //NSLog(@"springSlider value %f\n", springSliderCtl.value);
}

// For gesture zooming....

- (CGFloat)distanceBetweenTwoTouches: (UITouch *)fromTouch toTouch: (UITouch *)toTouch
{
    CGPoint fromPoint = [fromTouch locationInView: [fromTouch view]];
    fromPoint = [[Director sharedDirector] convertCoordinate: fromPoint];
    
    CGPoint toPoint = [toTouch locationInView: [toTouch view]];
    toPoint = [[Director sharedDirector] convertCoordinate: toPoint];
    
    float x = toPoint.x - fromPoint.x;
    float y = toPoint.y - fromPoint.y;
    
    return sqrt(x * x + y * y);
}

CGFloat initialDistance;
CGFloat finalDistance;
float zoomSliderValue = INIT_ZOOM_SLIDER_CTL_VALUE;
BOOL multiTouchP = NO;

- (BOOL)ccTouchesMoved: (NSSet *) __attribute__ ((unused)) touches withEvent: (UIEvent *)event {
    CGSize screenSize = [[Director sharedDirector] displaySize];
    NSSet *allTouches = [event allTouches];
    
    switch ([allTouches count]) {
        case 1:
            break;
        case 2: {
            if (multiTouchP == NO) break;
            //The image is being zoomed in or out.
            UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
            UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
            
            //Calculate the distance between the two fingers.
            finalDistance = [self distanceBetweenTwoTouches: touch1 toTouch: touch2];
            //NSLog(@"TouchesMoved: finalDistance = %f", finalDistance);
            
            //Check if zoom in or zoom out.
            zoomSliderValue += (finalDistance - initialDistance) / screenSize.width;

            // Must set Multiple Touch Enabled (see AppController in Menu.m) for the two finger
            // gestures to work...
            if(initialDistance > finalDistance) {
                //NSLog(@"Zoom In");
            } else {
                //NSLog(@"Zoom Out");
            }

            cameraZAxisZoomFactor = (ZOOMOUTLIMIT - ZOOMINLIMIT) * zoomSliderValue + ZOOMINLIMIT;
            //NSLog(@"TouchesMoved: zoomSliderValue %f  cameraZAxisZoomFactor %f\n", zoomSliderValue, cameraZAxisZoomFactor);
            if (cameraZAxisZoomFactor > ZOOMOUTLIMIT) cameraZAxisZoomFactor = ZOOMOUTLIMIT;
            if (cameraZAxisZoomFactor < ZOOMINLIMIT) cameraZAxisZoomFactor = ZOOMINLIMIT;
            initialDistance = finalDistance;
        }
            break;
    }
    return YES;
}

// Fired when the user first presses their finger on the screen...
- (BOOL)ccTouchesBegan:(NSSet *) __attribute__ ((unused)) touches withEvent:(UIEvent *) event {
    CGSize screenSize = [[Director sharedDirector] displaySize];
    NSSet *allTouches = [event allTouches];
    switch ([allTouches count]) {
            
        //Single touch
        case 1:
        {
            UITouch *touch = [[allTouches allObjects] objectAtIndex:0];
            
            // if you care about taps...
            switch ([touch tapCount]) {
                case 1: //Single Tap.
                        //NSLog(@"TouchesBegan: single touch; single tap.");
                    break;
                case 2: //Double tap.
                        //NSLog(@"TouchesBegan: single touch; double tap.");
                    break;
            }
            
            CGPoint loc = [touch locationInView: [touch view]];
            loc = [[Director sharedDirector] convertCoordinate: loc];
            if (loc.y < menuY && loc.x > screenSize.height - menuX) {
                // go to the main menu if the area on the screen where the button is positioned
                // has been touched...
                Scene * scene = [[Scene node] addChild: [Layer1 node] z:0];
                [[Director sharedDirector] pushScene: scene];
            }
        }
            break;
            
            //Double Touch
        case 2:
        {
            //Track the initial distance between two fingers.
            UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
            UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
            
            initialDistance = [self distanceBetweenTwoTouches: touch1 toTouch: touch2];
            //NSLog(@"TouchesBegan: double touch; initialDistance = %f", initialDistance);
            multiTouchP = YES;
        }
            break;
    }
    
    return YES;
}

- (BOOL)ccTouchesEnded:(NSSet *) __attribute__ ((unused)) touches withEvent:(UIEvent *) __attribute__ ((unused)) event {
    multiTouchP = NO;
    //NSLog(@"TouchesEnded");
    return YES;
}

- (void) draw {  
    NSEnumerator *e = [scoreImageView objectEnumerator];
    id iv;
    int i = currentScore;
    // Draw the number of lives that the user has. CurrentScore is always a bit larger
    // which takes into account the number of players that we started with.
    while (iv = [e nextObject]) {
        [iv setVisible: (i >= POINTS_PER_LIFE) ? YES : NO];
        i -= POINTS_PER_LIFE;
    }
    [menuSprite setVisible: YES];
    [labelAtlas setVisible: YES];
    
    // Update the score on the screen....
	NSString *str = [NSString stringWithFormat:@"%05d", currentScore, nil];
	[labelAtlas setString:str];
}

- (void)dealloc
{
    //[menuSprite release];
    //[labelAtlas release];
    [scoreImageView release];
    //[springSliderCtl release];

    [super dealloc];
}

@end



#define LEVEL_FILE_NAME         @"level%04d"

////
//// The layer with the game. This layer zooms...
////
@implementation GameLayer

@synthesize level;

//float zoomFactor = 0.0f;

// BUG: Need a better solution for handling the particle systems because
// they are leaked when the GameLayer is exited...

// This particle system is used when the pogostick pops a power-up pill,
// or get's hit by a bullet.
ParticleSystem *emitter = nil;

// This particle system is used to show thruse from the lunar lander...
ParticleSystem *thrustEmitter = nil;

// The current level number...
int levelNumber;

-(id) init {
	if ((self = [super init])) {
        
        // To allow the three touch methods to work...
        isTouchEnabled = YES;
        isAccelerometerEnabled = YES;
        
        amulet = nil;
        
        [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0f / 60.0f)];

        // start chipumnk, create the space for the bodies
        // and set the hash to a rough estimate of how many shapes there could be.
        // set gravity, make the physics run loop,
        // make a bounding box the size of the screen.
        cpInitChipmunk();
        cpSpace *space = cpSpaceNew();
        space->gravity = cpv(0.0f, SPACE_GRAVITY);
        space->iterations = 30;
        
        /* This step is optional. While you don't have to resize the spatial
         hashes, doing so can greatly increase the speed of the collision
         detection. The first number should be the expected average size of
         the objects you are going to have, the second number is related to
         the number of objects you are putting. In general, if you have more
         objects, you want the number to be bigger, but only to a
         point. Finding good numbers to use here is largely going to be guess
         and check. */
        cpSpaceResizeStaticHash(space, 8.0f, 200);
        cpSpaceResizeActiveHash(space, 8.0f, 30);
        
        emitter = [ParticleFlower node];
        [self addChild: emitter z:415.0f tag:kTagLabelEmitter];
        [emitter setTexture: [[TextureMgr sharedTextureMgr] addImage: @"stars.png"]];
        // Set the particle count to zero so it itn't seen till something happens
        // in the game.
        [emitter setTotalParticles: 0.0f];
#if 0
        // This is used for the space ship and should be moved into that object...
        thrustEmitter = [ParticleFireworks node];
        [self addChild: thrustEmitter z:415.0f tag:kTagLabelThrustEmitter];
        [thrustEmitter setTexture: [[TextureMgr sharedTextureMgr] addImage: @"fire.png"]];
        [thrustEmitter setAngle: 270.0f];
        [thrustEmitter setGravity: ccp(0.0f, -400.0f)];
        [thrustEmitter setTotalParticles: 0.0f];
#endif
        // Create the next level and all of the characters in it from the SVG file...
        // Use the level previously restored from the user defaults...

        NSError *error;
        NSString *fileName = [[NSString alloc] initWithFormat:LEVEL_FILE_NAME, levelNumber];
        //NSLog(@"Creating level from file: %s", [fileName UTF8String]);
        [self setLevel: [[Level alloc] initInSpace:space fileName:fileName ParseError:&error]];
        [fileName release];
        
        // set the slider effected variables by the default position of the slider...
        cameraZAxisZoomFactor = (ZOOMOUTLIMIT - ZOOMINLIMIT) * INIT_ZOOM_SLIDER_CTL_VALUE + ZOOMINLIMIT;
        stickSpringPower = 25.0f*INIT_SPRING_SLIDER_CTL_VALUE + 20.0f;
        
        [self schedule: @selector(step:)];
    }
	return self;
}

void drawCircleShape(cpShape *shape) {
	cpBody *body = shape->body;
	cpCircleShape *circle = (cpCircleShape *)shape;
	cpVect c = cpvadd(body->p, cpvrotate(circle->c, body->rot));
	drawCircle(ccp(c.x, c.y), circle->r, body->a, 25, NO);
    // !important this number changes the quality of circles
}

void drawSegmentShape(cpShape *shape) {
	cpBody *body = shape->body;
	cpSegmentShape *seg = (cpSegmentShape *)shape;
	cpVect a = cpvadd(body->p, cpvrotate(seg->a, body->rot));
	cpVect b = cpvadd(body->p, cpvrotate(seg->b, body->rot));
	drawLine(ccp(a.x, a.y), ccp(b.x, b.y));
}

void drawPolyShape(cpShape *shape) {
	cpBody *body = shape->body;
	cpPolyShape *poly = (cpPolyShape *)shape;
	
	int num = poly->numVerts;
	cpVect *verts = poly->verts;
	
	float *vertices = malloc( sizeof(float)*2*poly->numVerts);
	if(!vertices) return;
	
	for(int i=0; i<num; i++){
		cpVect v = cpvadd(body->p, cpvrotate(verts[i], body->rot));
		vertices[i*2] = v.x;
		vertices[i*2+1] = v.y;
	}
	drawPoly((CGPoint *)vertices, poly->numVerts, YES );
	free(vertices);
}

void drawObject(void *ptr, void *unused __attribute__ ((unused))) {
	cpShape *shape = (cpShape *)ptr;  
	glColor4f(1.0f, 1.0f, 1.0f, 0.7f);
	
	//if its the player
	if(shape->group == 55){
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	}
	
	switch(shape->klass->type){
		case CP_CIRCLE_SHAPE:
			glColor4f(0.2f, 0.6f, 0.4f, 1.0f);
			drawCircleShape(shape);
			break;
		case CP_SEGMENT_SHAPE:
			glColor4f(1.0f, 0.2f, 0.3f, 0.8f);
			drawSegmentShape(shape);
			break;
		case CP_POLY_SHAPE:
			drawPolyShape(shape);
			break;
		default:
            NSLog(@"Bad enumeration in drawObject().\n");
	}
}

// This method must be specialized, the default one does nothing.
- (void) draw {
    cpSpace *sp = [level space];
	cpSpaceHashEach(sp->activeShapes, &drawObject, NULL);
	cpSpaceHashEach(sp->staticShapes, &drawObject, NULL);
}

//#define cameraMagicNumber (1.1566f)
// See cocos2d iphone file glu.c
- (GLfloat) cameraMagicNumber:(CGSize)screenSize {
    GLfloat z[3];
	GLfloat centerX, centerY, centerZ;
	GLfloat eyeX, eyeY, eyeZ;
	
	[self.camera centerX:&centerX centerY:&centerY centerZ:&centerZ];
	[self.camera eyeX:&eyeX eyeY:&eyeY eyeZ:&eyeZ];
    
    z[0] = eyeX - centerX;
    z[1] = eyeY - centerY;
    z[2] = eyeZ - centerZ;
    GLfloat mag = (GLfloat)sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
    
	// return the quotient of the magnitude of the z vector and the screen height...
	return screenSize.height/mag;
}

// http://www.cocos2d-iphone.org/forum/topic/1314 
-(CGPoint)uiTouchToWorldCoordinates:(NSSet *)touches {
	UITouch *myTouch =  [touches anyObject];
	// translate for landscape
	CGPoint locScreen = [myTouch locationInView: [myTouch view]];
	locScreen = [[Director sharedDirector] convertCoordinate: locScreen];
	
	float centerX, centerY, centerZ;
	float eyeX, eyeY, eyeZ;
	[self.camera centerX:&centerX centerY:&centerY centerZ:&centerZ];
	[self.camera eyeX:&eyeX eyeY:&eyeY eyeZ:&eyeZ];
	
	CGSize screenSize = [[Director sharedDirector] displaySize];
    
	float ratio = 1.1566f/[self cameraMagicNumber: screenSize];
	CGPoint locWorld;
	locWorld.x = centerX + (locScreen.x - screenSize.width/2)*ratio;
	locWorld.y = centerY + (locScreen.y - screenSize.height/2)*ratio;
#if 0
	NSLog(@"center %f,%f,%f  eye %f,%f,%f  locScreen %f,%f  locWorld %f,%f  screenSize %f,%f\n",
          centerX, centerY, centerZ, eyeX, eyeY, eyeZ,
          locScreen.x, locScreen.y, locWorld.x, locWorld.y, screenSize.width, screenSize.height);
#endif
	return locWorld;
}


///
/// The finger movement on the touch screen will move the pogostick in the direction
/// of the finger swipe.

// The three methods below (ccTouchesBegan, ccTouchesMoved, ccTouchesEnd) form the basis
// of stateless touch screen management. see
// http://lethain.com/entry/2008/oct/20/touch-detection-in-cocos2d-iphone/

CGPoint lastTouch;
// Fired when the user first presses their finger on the screen...
- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *) __attribute__ ((unused)) event {
	lastTouch = [self uiTouchToWorldCoordinates: touches];
    return YES;
}

#define MOVE_FACTOR (10.0f)

// Fired while the finger is still pressed to the screen, but moving...
- (BOOL)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent*) __attribute__ ((unused)) event{  
	CGPoint location = [self uiTouchToWorldCoordinates: touches];
	//cpVect moved = cpvsub(cpv(lastTouch.x,lastTouch.y), cpv(location.x,location.y));
    cpVect moved = cpvsub(cpv(location.x,location.y), cpv(lastTouch.x,lastTouch.y));
    moved = cpvmult(moved, MOVE_FACTOR);
    cpBodyApplyImpulse([[level stick] stickBody], moved, cpvzero);

    //NSLog(@"Touch last %f,%f  this %f,%f  difference %f,%f\n", lastTouch.x, lastTouch.y, location.x, location.y, moved.x, moved.y);

    lastTouch = location;
    return YES;
}

// This is found in ChipmunkPhysics.m
extern int addPoints (int i);

// referenced by self schedule method above...
// This method is called by the scheduler for animation
-(void) step: (ccTime) delta {
	int steps = 2;
	cpFloat dt = delta/(cpFloat)steps;
    PogoStick *stick = [level stick];
	cpBody *sb = [stick stickBody];
    
	CGSize screenSize = [[Director sharedDirector] displaySize];
	//cpFloat lowestY = findLowestTerrainY(sb->p, screenSize.height);
    cpFloat lowestY = sb->p.y;
	
	float ratio = 1.1566f/[self cameraMagicNumber: screenSize];

    // remember that the screen is in landscape...
	float camX = sb->p.x;
	float camY = lowestY + (screenSize.width * 0.35f*ratio);
	camY = (sb->p.y < camY + screenSize.height*ratio) ? camY : sb->p.y;    
    
    // Used this before the slider control was put in to allow the program do the
    // zooming. It's tough to guess... letting the person do it is better I think.
	//cameraZAxisZoomFactor = (screenSize.height+zoomFactor) / [self cameraMagicNumber: screenSize];
	//if (cameraZAxisZoomFactor > zoomOutLimit) cameraZAxisZoomFactor = zoomOutLimit;
	//if (cameraZAxisZoomFactor < zoomInLimit) cameraZAxisZoomFactor = zoomInLimit;
	
	// The setEye method sets where the camera is looking FROM and the setCenter method
	// sets where the camera is looking TO. 
	// Position the camera center on the pogo stick with the Y set to include the lowest
	// point in the scene...
	[self.camera setCenterX:camX centerY:camY centerZ:0.0f];
	// The farther behind the camera the eye is placed (Z coordinate) the smaller things
	// will appear. The zoomfactor is computed in the 'zoomSliderAction'
	[self.camera setEyeX:camX eyeY:camY eyeZ:cameraZAxisZoomFactor];
    //NSLog(@"cameraZAxisZoomFactor %f\n", cameraZAxisZoomFactor);
    //NSLog(@"step:delta: cam %f,%f,%f screen <w,h> %f,%f\n", camX, camY, cameraZAxisZoomFactor, screenSize.width, screenSize.height);
    
	// This keeps Chipmunk from having problems if the delta get's big because of a garbage collection...
	if (delta > 0.25) return;
    
    // The stick and plunger should always be at the same angle...
    cpBody *f2b = [stick footBody];
    cpBodySetAngle(f2b, sb->a);
    
    // after the amulet is found, we an remove it from the game space...
    if ((amuletFound == YES) && (amulet != nil)) {
        [amulet release];
        amulet = nil;
    }
    
    // I have tried a number of algorithms to make the pogostick look like the foot has
    // a spring in it. This one is as good as any. When the foot first touches the ground
    // the footTouchedFloorCount is set to a number, on every call to this routine 1/60th
    // second that number is decremented till it's 0 and the stick is asked to jump!
    int cnt = [stick footTouchedFloorCount];
    if (cnt > 0) {
        [stick setFootTouchedFloorCount: --cnt];
        if (cnt == 0) {
            [stick jump];
        }
    }
    
    // Determine if the pogostick has reached the door to the next level...
    if ([level stickInDoor] == YES) {
        // get ready to load the next level from the appropriate SVG file...
        NSString *fileName = [[[NSString alloc] initWithFormat:LEVEL_FILE_NAME, ++levelNumber] autorelease];
        NSBundle *bundle = [NSBundle mainBundle];
        NSString *path = [[[bundle pathForResource:fileName ofType:@"svg"] retain] autorelease];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        
        // If there are no more levels... Determine if we won or lost!
        if (![fileManager fileExistsAtPath:path]) {
            extern BOOL resetGameOnPlay; // from menu.m
            extern BOOL gameStarted; // from menu.m
            /*
             This must be done just as soon as we know that the next level
             is not availale. If the user stops the app in between it will crash
             the next time it is run...
             */
            gameStarted = NO; // There is no active game at this point...
            resetGameOnPlay = YES;// they got out of all of the levels...
            
            currentScore += addPoints(POINTS_FOR_COMPLETING);
            Scene *scene = [[Scene node] addChild:((amuletFound == YES) ? [YouWon node] : [YouLost node]) z:0];
            [[Director sharedDirector] replaceScene: scene];
            return;
        }
        
        // create a new space and level according to the SVG file information...
        cpSpace *space = cpSpaceNew();
        space->gravity = cpv(0.0f, SPACE_GRAVITY);
        space->iterations = 30;
        cpSpaceResizeStaticHash(space, 5.0f, 500);
        cpSpaceResizeActiveHash(space, 5.0f, 300);
        NSError *error;
        [self setLevel: [[Level alloc] initInSpace:space fileName:fileName ParseError:&error]];
    }
    
	for(int i=0; i<steps; i++){
		// delete objects that should be removed from the colission call back...
		// remove Spent pills must be called before cpSpaceStep...
        // This method will call the same method for all of the Floors that have been
        // created for this level.
        // ParticleSystem *emitter = (ParticleSystem *) [self getChildByTag:kTagLabelEmitter];
		// [level processQueuedStickHitPillEventsInSpace:space Emitter:emitter];
        
        // This method calls all of the other action methods of all things attached to the layer
        // that have motion...
        if ([level performActions] == NO) {
            Scene *scene = [[Scene node] addChild: [NoMoreLives node] z:0];
            [[Director sharedDirector] replaceScene: scene];
            return;
        }
        
		cpSpaceStep([level space], dt);
	}
}

- (void)dealloc
{ 
    [self removeChild:emitter cleanup:NO];
    //[self removeChild:thrustEmitter cleanup:NO];
    //[self cleanup];
    [level release];
    [amulet release];
    
    [super dealloc];
}


///
/// The angle of the pogostick is determined by the tilt of the device...

- (void)accelerometer:(UIAccelerometer*) __attribute__ ((unused)) accelerometer didAccelerate:(UIAcceleration*)acceleration {
    
    // Convert the coordinates to 'landscape' coords since they are always in 'portrait' coordinates
    CGPoint converted = ccp( (float)-acceleration.y, (float)acceleration.x);
    
    //int deltaX = (converted.y<=0)? sprite.position.x-2:sprite.position.x+2;
    //sprite.position = ccp(deltaX,sprite.position.y);
    
    // update the rotation based on the z-rotation
    // the sprite will always be 'standing up'
    cpFloat rotation = atan2f(converted.x, converted.y) - M_PI;
    // The value should always be between -M_PI and M_PI.
    // This works OK for the stick in the usual position (pointing down)
    // but if you rotate it "over the top" it will flip.
    while (rotation < -M_PI || rotation > M_PI) {
        if (rotation < -M_PI) rotation += 2*M_PI;
        if (rotation > M_PI) rotation -= 2*M_PI;
    }
    // Reverse the direction of movement so that the stick is in phase with the turning of the screen...
    //NSLog(@"accelerometer rotation %f\n", -rotation);
    // Change the orientation of the stick to match how the screen is tilted...
    [[level stick] orientation: -rotation];
}

@end
